package com.ala4.oxcafe.boot.interceptor;

import com.ala4.oxcafe.common.utils.collection.CollUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.beans.factory.annotation.Value;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.text.DateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;

/**
 * 打印SQL拦截器-直接打印拼装好的sql
 *
 * @author PING
 * @version 1.0.0
 * @date 2024/12/6 10:30
 */
@Slf4j
@Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class NewPrintSqlInterceptor implements Interceptor {

    @Value("${mybatis.show-log:false}")
    private Boolean showLog;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        log.debug("NewPrintSqlInterceptor intercept invoked.");
        if (Boolean.FALSE.equals(showLog)) {
            return invocation.proceed();
        }

        String msId = "无法获取到MSID";
        // 获取目标对象
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        // 如果是代理类
        if (Proxy.isProxyClass(statementHandler.getClass())) {
            // 获取真实代理对象
            InvocationHandler invocationHandler = Proxy.getInvocationHandler(statementHandler);

            MetaObject metaObject = SystemMetaObject.forObject(invocationHandler);
            if (metaObject.hasGetter("target.delegate.mappedStatement")) {
                MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("target.delegate.mappedStatement");
                msId = mappedStatement.getId();
            }
        }


        // 获取BoundSql对象
        BoundSql boundSql = statementHandler.getBoundSql();

        long start = System.currentTimeMillis();
        Object returnValue = invocation.proceed();
        long time = System.currentTimeMillis() - start;
        showSql(boundSql, time, msId);

        return returnValue;
    }

    private static void showSql(BoundSql boundSql, long time, String sqlId) {
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        //替换空格、换行、tab缩进等
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");

        if (CollUtil.isNotEmpty(parameterMappings) && parameterObject != null) {
            MetaObject metaObject = SystemMetaObject.forObject(parameterObject);
            for (ParameterMapping parameterMapping : parameterMappings) {
                String propertyName = parameterMapping.getProperty();
                if (metaObject.hasGetter(propertyName)) {
                    Object obj = metaObject.getValue(propertyName);
                    sql = sql.replaceFirst("\\?", getParameterValue(obj));
                } else if (boundSql.hasAdditionalParameter(propertyName)) {
                    Object obj = boundSql.getAdditionalParameter(propertyName);
                    sql = sql.replaceFirst("\\?", getParameterValue(obj));
                }
            }
        }
        logs(time, sql, sqlId);
    }

    private static String getParameterValue(Object obj) {
        String value;
        if (obj instanceof String) {
            value = "'" + obj + "'";
        } else if (obj instanceof Date) {
            DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
            value = "'" + formatter.format(new Date()) + "'";
        } else {
            if (obj != null) {
                value = obj.toString();
            } else {
                value = "";
            }
        }
        return value.replace("$", "\\$");
    }

    private Object getRealTarget(Object target) {
        Object current = target;
        while (isProxy(current)) {
            MetaObject metaObject = SystemMetaObject.forObject(current);
            // 尝试不同的代理字段
            if (metaObject.hasGetter("h")) {
                current = metaObject.getValue("h");
            } else if (metaObject.hasGetter("target")) {
                current = metaObject.getValue("target");
            } else {
                break;
            }
        }
        return current;
    }

    /**
     * 判断对象是否为代理对象
     */
    private boolean isProxy(Object object) {
        if (object == null) return false;
        String className = object.getClass().getName();
        // 常见的代理类标识
        return className.contains("$Proxy") ||
                className.contains("EnhancerBy") ||
                className.contains("CGLIB$$");
    }

    private static void logs(long time, String sql, String sqlId) {
        log.info("===============Start Print SQL===============");
        log.info("Mapper Method: [{}]", sqlId);
        log.info("Execute SQL  : {}", sql);
        log.info("Spend Time   : [{}ms]", time);
        log.info("===============End   Print SQL===============");
    }
}
